package versionControl.aspectj.library;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Vector;
import com.ajexperience.utils.DeepCopyException;
import com.ajexperience.utils.DeepCopyUtil;

/*
 * Adds version control functionality to any object
 * Supports CreateNew, Commit, Rollback, Update and Lock operations
 */
public aspect Version 
{
	// Keeps the version ids of existing versions (not all the versions exist at all times (aka delete)
	// used mainly for rollback operation when ordering is important
	private Vector<Integer> Versioned.versionsIDs = new Vector<Integer>();
	// Keeps a reference to all the versions of the instance by version id for effective lookup
	private HashMap<Integer, Versioned> Versioned.versions = new HashMap<Integer, Versioned>();
	
	private int Versioned.IDCounter = 0;
	private int Versioned.versionID = 0;
	private Date Versioned.timeStamp;
	private String Versioned.comment;

	// Returns the timestamp of the version's creation
	public Date Versioned.getTimeStamp()
	{
		return timeStamp;	
	}
	
	public void Versioned.setTimeStamp(Date time)
	{
		timeStamp = time;	
	}
	
	// Returns the comment attached to the version
	public String Versioned.getComment()
	{
		return comment;	
	}
	
	public void Versioned.setComment(String cmt)
	{
		comment = cmt;	
	}
	
	// Returns all object stored versions
	public HashMap<Integer,Versioned> Versioned.getVersions()
	{
		return versions;
	}
	
	public void Versioned.setVersions(HashMap<Integer, Versioned> newVersions)
	{
		versions = newVersions;
	}
		
	// Returns the object version id
	public int Versioned.getVersionID()
    { 
    	return versionID; 
    }
	
	public void Versioned.setVersionID(int id)
    { 
    	versionID = id; 
    } 
	
	// Returns the id of the last added version
	public final int Versioned.getLatestVersionID()
	{
		if (!versionsIDs.isEmpty())
			return versionsIDs.lastElement();
		else return -1;
	}
  
	public String Versioned.toString()
    {
            return new String("code: " + this.hashCode() + "versionID: " + this.getVersionID() + this.toString());
    }
	
	// Delete the given version according to its version id
	// from the object version control
	public void Versioned.deleteVersion(int id) 
	{ 					
		if (null != this.versions.remove(id)){
			int deleteId = -1;
			for(Integer verId: versionsIDs)
			{
				if(verId == id)
					deleteId = verId;
			}
			if (deleteId != -1)
				this.versionsIDs.remove(deleteId);
		}
	} 
	
	// Adds a new version to the object control
	// returns the new version id
	public int Versioned.newVersion()
	{		
		try{
			DeepCopyUtil deepCopyUtil = new DeepCopyUtil();
			Versioned versionedObject = deepCopyUtil.deepCopy(this);
			
			this.IDCounter++;
			versionedObject.setVersionID(IDCounter);
			versionedObject.setTimeStamp(new Date());

			this.addVersion(versionedObject); 
					
			return versionedObject.getVersionID();
		}
		catch(DeepCopyException e){
			e.printStackTrace();
		}
		
		return -1;
	}
	
	// Update the version control with a given version 
	public void Versioned.addVersion(Versioned newObject)
	{
		this.versions.put(newObject.getVersionID(), newObject);
		this.versionsIDs.add(newObject.getVersionID());
	}
	
	// Returns the version according to its version id
	public Versioned Versioned.getVersionByID(int id)
	{
		return this.versions.get(id);
	}
	
	// Invoke a method by reflection on the given version id
	public Object Versioned.invokeMethod(int objID, String methodName, Object[] parameters)						
	{
		try{
			Object object = versions.get(objID);
			if (null != object){
				Method method = null;
				for(Method m: object.getClass().getMethods())
				{
					if(m.getName().equals(methodName))
					{
						method = m;
						break;
					}
				}
				if (null != method)
					return method.invoke(object,parameters);
			}
		}
		catch(IllegalAccessException e)
		{
			e.printStackTrace();
		}
		catch(IllegalArgumentException e)
		{
			e.printStackTrace();
		}
		catch(InvocationTargetException e)
		{
			e.printStackTrace();
		}
		return null;
	}	 
	
	// Updates a field by reflection on the given version id 
	public void Versioned.UpdateField(int objID, String fieldName, Object[] parameters)						
	{
		try{
			Object object = versions.get(objID);
			if (null != object){
				Field field = null;
				for (Field f: object.getClass().getFields())
				{
					if (f.getName().equals(fieldName))
					{
						field = f;								
						break;
					}
				}
				if(null != field)
					field.set(object, parameters[0]);
			}
		}
		catch(IllegalAccessException e)
		{
			e.printStackTrace();
		}
		catch(IllegalArgumentException e)
		{
			e.printStackTrace();
		}	
	}	
}